using System;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;

namespace RayDen.Library.Core.Primitives
{
    public class RgbSpectrumInfo : IRgbConvertible, ISerializableValue, IStringSerializable, ISpectrum
    {
        public static readonly RgbSpectrumInfo Unit = new RgbSpectrumInfo(1f);

        [DataMember]
        public float c1;
        [DataMember]
        public float c2;
        [DataMember]
        public float c3;

        public const float Epsilon = 0.00000001f;
        static int SamplesCount = 3;

        public float this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0:
                        return c1;
                    case 1:
                        return c2;
                    case 2:
                        return c3;
                    default:
                        throw new NotImplementedException();
                }
            }
            set
            {
                switch (index)
                {
                    case 0:
                        c1 = value;
                        break;
                    case 1:
                        c2 = value;
                        break;
                    case 2:
                        c3 = value;
                        break;
                    default:
                        throw new NotImplementedException();
                }
            }
        }

        public RgbSpectrumInfo() : this(0f)
        {            
        }

        public RgbSpectrumInfo(float a)
        {
#if DEBUG
            RayDen.Library.Core.Components.Assert.IsNotNaN(a);
#endif
            c1 = c2 = c3 = a;
        }

        public RgbSpectrumInfo(float r, float g, float b)
        {
#if DEBUG
            RayDen.Library.Core.Components.Assert.IsNotNaN(r);
            RayDen.Library.Core.Components.Assert.IsNotNaN(g);
            RayDen.Library.Core.Components.Assert.IsNotNaN(b);
#endif
            c1 = r;
            c2 = g;
            c3 = b;
        }

        public RgbSpectrumInfo(float[] d)
        {
            c1 = d[0];
            c2 = d[1];
            c3 = d[2];
#if DEBUG
            RayDen.Library.Core.Components.Assert.IsNotNaN(c1);
            RayDen.Library.Core.Components.Assert.IsNotNaN(c2);
            RayDen.Library.Core.Components.Assert.IsNotNaN(c3);
#endif
        }

        public RgbSpectrumInfo(Vector v)
        {
            c1 = v.x;
            c2 = v.y;
            c3 = v.z;
#if DEBUG
            RayDen.Library.Core.Components.Assert.IsNotNaN(c1);
            RayDen.Library.Core.Components.Assert.IsNotNaN(c2);
            RayDen.Library.Core.Components.Assert.IsNotNaN(c3);
#endif
        }
        public RgbSpectrumInfo(ref Vector v)
        {
            c1 = v.x;
            c2 = v.y;
            c3 = v.z;
#if DEBUG
            RayDen.Library.Core.Components.Assert.IsNotNaN(c1);
            RayDen.Library.Core.Components.Assert.IsNotNaN(c2);
            RayDen.Library.Core.Components.Assert.IsNotNaN(c3);
#endif
        }
        public RgbSpectrumInfo(ref RgbSpectrum sp)
        {
            c1 = sp.c1;
            c2 = sp.c2;
            c3 = sp.c3;
#if DEBUG
            RayDen.Library.Core.Components.Assert.IsNotNaN(c1);
            RayDen.Library.Core.Components.Assert.IsNotNaN(c2);
            RayDen.Library.Core.Components.Assert.IsNotNaN(c3);
#endif
        }


        #region Operators

        public static explicit operator RgbSpectrum(RgbSpectrumInfo sp)
        {
            return new RgbSpectrum(sp.c1, sp.c2, sp.c3);
        }

        public static explicit operator RgbSpectrumInfo(RgbSpectrum sp)
        {
            return new RgbSpectrumInfo(sp);
        }
       
        public static explicit operator RgbSpectrumInfo(Vector sp)
        {
            return new RgbSpectrumInfo(ref sp);
        }

        public static RgbSpectrumInfo operator +(RgbSpectrumInfo a, RgbSpectrumInfo b)
        {
            return new RgbSpectrumInfo(a.c1 + b.c1, a.c2 + b.c2, a.c3 + b.c3);
        }

        public static RgbSpectrumInfo operator +(RgbSpectrumInfo a, Vector b)
        {
            return new RgbSpectrumInfo(a.c1 + (float)b.x, a.c2 + (float)b.y, a.c3 + (float)b.z);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static RgbSpectrumInfo operator *(RgbSpectrumInfo a, RgbSpectrumInfo b)
        {
            return new RgbSpectrumInfo(a.c1 * b.c1, a.c2 * b.c2, a.c3 * b.c3);
        }

        public static RgbSpectrumInfo operator /(RgbSpectrumInfo a, RgbSpectrumInfo b)
        {
            return new RgbSpectrumInfo(a.c1 / b.c1, a.c2 / b.c2, a.c3 / b.c3);
        }

        public static RgbSpectrumInfo operator -(RgbSpectrumInfo a, RgbSpectrumInfo b)
        {
            return new RgbSpectrumInfo(a.c1 - b.c1, a.c2 - b.c2, a.c3 - b.c3);
        }

        public static RgbSpectrumInfo operator *(RgbSpectrumInfo a, Vector b)
        {
            return new RgbSpectrumInfo(a.c1 * (float)b.x, a.c2 * (float)b.y, a.c3 * (float)b.z);
        }

        public static RgbSpectrumInfo operator *(Vector b, RgbSpectrumInfo a)
        {
            return new RgbSpectrumInfo(a.c1 * (float)b.x, a.c2 * (float)b.y, a.c3 * (float)b.z);
        }

        public static RgbSpectrumInfo operator *(RgbSpectrumInfo a, float b)
        {
            return new RgbSpectrumInfo(a.c1 * b, a.c2 * b, a.c3 * b);
        }


        public static RgbSpectrumInfo operator +(RgbSpectrumInfo a, float b)
        {
            return new RgbSpectrumInfo(a.c1 + b, a.c2 + b, a.c3 + b);
        }
        public static RgbSpectrumInfo operator +(float b, RgbSpectrumInfo a)
        {
            return new RgbSpectrumInfo(a.c1 + b, a.c2 + b, a.c3 + b);
        }
        public static RgbSpectrumInfo operator -(RgbSpectrumInfo a, float b)
        {
            return new RgbSpectrumInfo(a.c1 - b, a.c2 - b, a.c3 - b);
        }

        public static RgbSpectrumInfo operator -(float b, RgbSpectrumInfo a)
        {
            return new RgbSpectrumInfo(b - a.c1, b - a.c2, b - a.c3);
        }
        public static RgbSpectrumInfo operator *(float b, RgbSpectrumInfo a)
        {
            return new RgbSpectrumInfo(a.c1 * b, a.c2 * b, a.c3 * b);
        }

        public static RgbSpectrumInfo operator /(RgbSpectrumInfo a, float b)
        {
            float k = 1f / b;
            return new RgbSpectrumInfo(a.c1 * k, a.c2 * k, a.c3 * k);
        }

        public static RgbSpectrumInfo operator /(float b, RgbSpectrumInfo a)
        {
            return new RgbSpectrumInfo(b / a.c1, b / a.c2, b / a.c3);
        }

        #region Double operators



        public static RgbSpectrumInfo operator +(RgbSpectrumInfo a, double b)
        {
            return new RgbSpectrumInfo((float)(a.c1 + b), (float)(a.c2 + b), (float)(a.c3 + b));
        }

        public static RgbSpectrumInfo operator +(double b, RgbSpectrumInfo a)
        {
            return new RgbSpectrumInfo((float)(a.c1 + b), (float)(a.c2 + b), (float)(a.c3 + b));
        }

        public static RgbSpectrumInfo operator -(RgbSpectrumInfo a, double b)
        {
            return new RgbSpectrumInfo((float)(a.c1 - b), (float)(a.c2 - b), (float)(a.c3 - b));
        }

        public static RgbSpectrumInfo operator -(double b, RgbSpectrumInfo a)
        {
            return new RgbSpectrumInfo((float)(b - a.c1), (float)(b - a.c2), (float)(b - a.c3));
        }
        public static RgbSpectrumInfo operator *(double b, RgbSpectrumInfo a)
        {
            return new RgbSpectrumInfo((float)(a.c1 * b), (float)(a.c2 * b), (float)(a.c3 * b));
        }
        public static RgbSpectrumInfo operator *(RgbSpectrumInfo a, double b)
        {
            return new RgbSpectrumInfo((float)(a.c1 * b), (float)(a.c2 * b), (float)(a.c3 * b));
        }
        public static RgbSpectrumInfo operator /(RgbSpectrumInfo a, double b)
        {
            double k = 1 / b;
            return new RgbSpectrumInfo((float)(a.c1 * k), (float)(a.c2 * k), (float)(a.c3 * k));
        }

        public static RgbSpectrumInfo operator /(double b, RgbSpectrumInfo a)
        {
            return new RgbSpectrumInfo((float)(b / a.c1), (float)(b / a.c2), (float)(b / a.c3));
        }
        #endregion

        #endregion

        #region Properties

        const float OneThird = 1f / 3f;
        public float Average
        {
            get
            {
                return (this.c1 + this.c2 + this.c3) * OneThird;
            }
        }

        public float Length
        {
            get
            {
                return MathLab.Sqrt(this.c1 * this.c1 + this.c2 * this.c2 + this.c3 * this.c3);
            }
        }


        public float y()
        {
            float v = c1 * YWeight[0] + c2 * YWeight[1] + c3 * YWeight[2];
            return v;
        }
        #endregion

        public RgbSpectrumInfo Abs()
        {
            return new RgbSpectrumInfo(Math.Abs(c1), Math.Abs(c2), Math.Abs(c3));
        }

        public void Sum(Vector v1)
        {
            c1 += (float)v1.x;
            c2 += (float)v1.y;
            c3 += (float)v1.z;
        }

        public void Sum(RgbSpectrum v1)
        {
            c1 += v1[0];
            c2 += v1[1];
            c3 += v1[2];
        }


        public RgbSpectrumInfo Sqrt()
        {
            for (int i = 0; i < 3; i++)
            {
                this[i] = MathLab.Sqrt(this[i]);
            }



            return this;
        }

        public static RgbSpectrumInfo Sqrt(ref RgbSpectrumInfo r)
        {
            for (int i = 0; i < 3; i++)
            {
                r[i] = MathLab.Sqrt(r[i]);
            }

            return r;
        }

        public RgbSpectrumInfo Apply(Func<float, float> modifier)
        {
            return new RgbSpectrumInfo(
                modifier(this.c1),
                modifier(this.c2),
                modifier(this.c3));
        }

        public void Modify(Func<float, float> modifier)
        {
            this.c1 = modifier(this.c1);
            this.c2 = modifier(this.c2);
            this.c3 = modifier(this.c3);
        }
        public void Clip(float min, float max)
        {

            if (c1 < min || float.IsNaN(c1))
                c1 = min;
            else if (c1 > max)
                c1 = max;

            if (c2 < min || float.IsNaN(c2))
                c2 = min;
            else if (c2 > max)
                c2 = max;

            if (c3 < min || float.IsNaN(c3))
                c3 = min;
            else if (c3 > max)
                c3 = max;

        }

        public static float GammaValue = 1.8f;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void GammaCorrection()
        {
            //RgbSpectrum res = new RgbSpectrum(this);

            float EXP = 1.0f / GammaValue;
            float a = 0.055f;
            float b = (1.0f + a);

            //for (int i = 0; i < 3; i++)
            //{
            //    if (res[i] < 0.0031308f)
            //        res[i] = 12.92f * res[i];
            //    else
            //    {
            //        res[i] = b*(float) MathLab.Pow(res[i], EXP) - a;
            //    }
            //}


            if (c1 < 0.0031308f)
                c1 = 12.92f * c1;
            else
            {
                c1 = b * (float)MathLab.Pow(c1, EXP) - a;
            }


            if (c2 < 0.0031308f)
                c2 = 12.92f * c2;
            else
            {
                c2 = b * (float)MathLab.Pow(c2, EXP) - a;
            }

            if (c3 < 0.0031308f)
                c3 = 12.92f * c3;
            else
            {
                c3 = b * (float)MathLab.Pow(c3, EXP) - a;
            }
        }
        public static bool Gamma = false;

        public Color ToColor()
        {
            Clip(0.0f, 1.0f);
            return Color.FromArgb((int)(c1 * 255.0f), (int)(c2 * 255.0f), (int)(c3 * 255.0f));
        }


        /// <summary>
        /// Performs transformation linear spectra -> XYZ -> RGB
        /// </summary>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public RgbSpectrumInfo Transform()
        {
            if (Gamma)
            {
                GammaCorrection();
            }
            float[] xyz = new float[3];
            XYZ(ref xyz);

            float[] rgb;
            SampledSpectrum.XYZToRGB(xyz, out rgb);
            return new RgbSpectrumInfo(rgb);
        }

        public static RgbSpectrumInfo Transform(RgbSpectrumInfo c)
        {
            float[] xyz = new float[3];
            c.XYZ(ref xyz);
            var r = ((rWeight[0] * xyz[0] +
                              rWeight[1] * xyz[1] +
                              rWeight[2] * xyz[2]));
            var g = ((gWeight[0] * xyz[0] +
                              gWeight[1] * xyz[1] +
                              gWeight[2] * xyz[2]));
            var b = ((bWeight[0] * xyz[0] +
                              bWeight[1] * xyz[1] +
                              bWeight[2] * xyz[2]));
            return new RgbSpectrumInfo(r, g, b);
        }
        public RgbSpectrumInfo ToRgb()
        {

            return Transform();
            //const float rangeScale = 255.0f;
            //RgbSpectrum c = Gamma ? GammaCorrection() : this;
            /*
            float[] xyz = this.ToArray();
            //c._XYZ(ref xyz);
            var r = ((rWeight[0] * xyz[0] +
                              rWeight[1] * xyz[1] +
                              rWeight[2] * xyz[2]));
            var g = ((gWeight[0] * xyz[0] +
                              gWeight[1] * xyz[1] +
                              gWeight[2] * xyz[2]));
            var b = ((bWeight[0] * xyz[0] +
                              bWeight[1] * xyz[1] +
                              bWeight[2] * xyz[2]));
            return new RgbSpectrum(r, g, b);*/
        }

        public void TransformToRgb()
        {
            float x, y, z;


            XYZ(out x, out y, out z);

            c1 = ((rWeight[0] * x + rWeight[1] * y + rWeight[2] * z));
            c2 = ((gWeight[0] * x + gWeight[1] * y + gWeight[2] * z));
            c3 = ((bWeight[0] * x + bWeight[1] * y + bWeight[2] * z));
        }

        public void TransformToRgb(out RgbSpectrum result)
        {

            float x, y, z;


            XYZ(out x, out y, out z);
            result = new RgbSpectrum
                {
                    c1 = ((rWeight[0] * x + rWeight[1] * y + rWeight[2] * z)),
                    c2 = ((gWeight[0] * x + gWeight[1] * y + gWeight[2] * z)),
                    c3 = ((bWeight[0] * x + bWeight[1] * y + bWeight[2] * z))
                };
        }

        public void AssertNaN()
        {
            var alert = float.IsNaN(c1) || float.IsNaN(c2) || float.IsNaN(c3);
            if (alert)
            {
                Debugger.Break();
            }
        }

        public override string ToString()
        {
            return string.Format("{0}, {1}, {2}", c1, c2, c3);
        }

        public string ToString(string sep)
        {
            return string.Format("{0}{3}{1}{3}{2}", c1, c2, c3, sep);
        }


        public static RgbSpectrumInfo Create(Color clrColor)
        {
            return new RgbSpectrumInfo(Math.Abs(clrColor.R / 255.0f), Math.Abs(clrColor.G / 255.0f), Math.Abs(clrColor.B / 255.0f));
        }

        public static RgbSpectrumInfo FromXYZ(float[] xyz)
        {
            var nsum = xyz[0] + xyz[1] + xyz[2];
            return new RgbSpectrumInfo(xyz[0] / nsum, xyz[1] / nsum, xyz[2] / nsum);
        }

        public static RgbSpectrumInfo Create(byte clrColorR, byte clrColorG, byte clrColorB, bool correct)
        {

            var cl = new RgbSpectrumInfo((float)clrColorR / 255.0f, (float)clrColorG / 255.0f, (float)clrColorB / 255.0f);

            if (correct) return cl;

            cl.c1 = (float)Math.Log(1.001f - cl.c1);
            cl.c2 = (float)Math.Log(1.001f - cl.c2);
            cl.c3 = (float)Math.Log(1.001f - cl.c3);
            return cl;
        }

        public static RgbSpectrumInfo UnitSpectrum()
        {
            return new RgbSpectrumInfo(1f);
        }

        unsafe public static RgbSpectrumInfo Create(int* pixels, int width, int height, float u, float v)
        {
            return Create(Color.FromArgb(pixels[((int)(v * (height - 1))) * width + ((int)(u * (width - 1)))]));
        }

        public static RgbSpectrumInfo Create(Bitmap bitmap, float u, float v)
        {
            return Create(bitmap.GetPixel((int)(u * (bitmap.Width - 1)),
                (int)(v * (bitmap.Height - 1))));
        }

        #region Instance Operations

        public void Add(ref RgbSpectrumInfo a)
        {
            this.c1 += a.c1;
            this.c2 += a.c2;
            this.c3 += a.c3;
        }

        public RgbSpectrumInfo Add(ref Vector a)
        {
            this.c1 += (float)a.x;
            this.c2 += (float)a.y;
            this.c3 += (float)a.z;

            return this;
        }

        public RgbSpectrumInfo Sub(ref RgbSpectrumInfo a)
        {
            this.c1 -= a.c1;
            this.c2 -= a.c2;
            this.c3 -= a.c3;

            return this;
        }


        public void Mul(ref ISpectrum t)
        {
            var a = (RgbSpectrum)t;
            this.c1 *= a.c1;
            this.c2 *= a.c2;
            this.c3 *= a.c3;

        }
        public RgbSpectrumInfo Mul(ref RgbSpectrum a)
        {
            this.c1 *= a.c1;
            this.c2 *= a.c2;
            this.c3 *= a.c3;
            return this;
        }

        public RgbSpectrumInfo Mul(RgbSpectrumInfo a)
        {
            this.c1 *= a.c1;
            this.c2 *= a.c2;
            this.c3 *= a.c3;
            return this;

        }
        public RgbSpectrumInfo Div(RgbSpectrumInfo a)
        {
            this.c1 /= a.c1;
            this.c2 /= a.c2;
            this.c3 /= a.c3;

            return this;
        }

        public void Add(float a)
        {
            this.c1 += a;
            this.c2 += a;
            this.c3 += a;

        }

        public RgbSpectrumInfo Sub(float a)
        {
            this.c1 -= a;
            this.c2 -= a;
            this.c3 -= a;

            return this;
        }

        public void Mul(float a)
        {
            this.c1 *= a;
            this.c2 *= a;
            this.c3 *= a;

        }

        public void Mulp(float a)
        {
            this.c1 *= a;
            this.c2 *= a;
            this.c3 *= a;
        }
        public void Div(float a)
        {
            float d = 1f / a;

            this.c1 *= d;
            this.c2 *= d;
            this.c3 *= d;

        }


        #endregion



        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsBlack()
        {
            return (c1 + c2 + c3) < Epsilon;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsNegative()
        {
            return this.c1 < 0f | c2 < 0f | c3 < 0f;
        }

        public static RgbSpectrumInfo Black
        {
            get { return new RgbSpectrumInfo(Epsilon); }
        }


        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public float[] ToArray()
        {
            return new[] { c1, c2, c3 };
        }

        public static RgbSpectrumInfo   ZeroSpectrum()
        {
            return new RgbSpectrumInfo(0f);
        }

        public static RgbSpectrumInfo Red
        {
            get { return new RgbSpectrumInfo(1f, 0f, 0f); }
        }

        public static RgbSpectrumInfo Green
        {
            get { return new RgbSpectrumInfo(0f, 1f, 0f); }
        }

        internal bool IsZero()
        {
            return this.ToArray().All(p => p < Epsilon);
        }

        public float Dot(ref Vector RGB_LUMINANCE)
        {
            return c1 * (float)RGB_LUMINANCE.x + c2 * (float)RGB_LUMINANCE.y + c3 * (float)RGB_LUMINANCE.z;
        }

        public float Dot(Vector RGB_LUMINANCE)
        {
            return c1 * (float)RGB_LUMINANCE.x + c2 * (float)RGB_LUMINANCE.y + c3 * (float)RGB_LUMINANCE.z;
        }
        public RgbSpectrumInfo Pow(RgbSpectrumInfo e)
        {
            RgbSpectrumInfo ret = new RgbSpectrumInfo();
            for (int i = 0; i < SamplesCount; ++i)
                ret[i] = this[i] > 0 ? (float)Math.Pow((float)this[i], (float)e[i]) : 0f;
            return ret;
        }

        public static RgbSpectrumInfo Exp(RgbSpectrumInfo s)
        {
            RgbSpectrumInfo ret = new RgbSpectrumInfo();
            for (int i = 0; i < SamplesCount; ++i)
                ret[i] = (float)Math.Exp((float)s[i]);
            return ret;
        }

        public RgbSpectrumInfo Clamp(float low,
                       float high)
        {
            RgbSpectrumInfo ret = new RgbSpectrumInfo();
            for (int i = 0; i < SamplesCount; ++i)
                ret[i] = Clamp(this[i], low, high);
            return ret;
        }

        private float Clamp(float p, float low, float high)
        {
            float result = p;

            if (p > high)
                result = high;
            if (p < low)
                result = low;

            return result;
        }


        public void ToXYZ()
        {
            float[] xyz = new float[3];
            xyz[0] = xyz[1] = xyz[2] = 0f;
            for (int i = 0; i < 3; ++i)
            {
                xyz[0] += XWeight[i] * this[i];
                xyz[1] += YWeight[i] * this[i];
                xyz[2] += ZWeight[i] * this[i];
            }
            c1 = xyz[0];
            c2 = xyz[1];
            c3 = xyz[2];
        }

        public void XYZToRGB()
        {
            float[] rgb = new float[3];
            rgb[0] = rgb[1] = rgb[2] = 0f;
            for (int i = 0; i < 3; ++i)
            {
                rgb[0] += rWeight[i] * this[i];
                rgb[1] += gWeight[i] * this[i];
                rgb[2] += bWeight[i] * this[i];
            }
            c1 = rgb[0];
            c2 = rgb[1];
            c3 = rgb[2];
        }

        public void XYZ(ref float[] xyz)
        {
            //_XYZ(ref xyz);
            //return;
            xyz[0] = xyz[1] = xyz[2] = 0f;
            for (int i = 0; i < 3; ++i)
            {
                xyz[0] += XWeight[i] * this[i];
                xyz[1] += YWeight[i] * this[i];
                xyz[2] += ZWeight[i] * this[i];
            }
        }

        public void XYZ(out float x, out float y, out float z)
        {

            x = y = z = 0f;
            //for (int i = 0; i < 3; ++i)
            {
                x += XWeight[0] * c1 + XWeight[1] * c2 + XWeight[2] * c3;
                y += YWeight[0] * c1 + YWeight[1] * c2 + YWeight[2] * c3;
                z += ZWeight[0] * c1 + ZWeight[1] * c2 + ZWeight[2] * c3;
            }
        }

        public void _XYZ(ref float[] xyz)
        {
            xyz[0] = xyz[1] = xyz[2] = 0f;
            xyz[0] = XWeight[0] * c1 + XWeight[1] * c2 + XWeight[2] * c3;
            xyz[1] = YWeight[0] * c1 + YWeight[1] * c2 + YWeight[2] * c3;
            xyz[2] = ZWeight[0] * c1 + ZWeight[1] * c2 + ZWeight[2] * c3;

        }

        public float[] XYZ()
        {
            var xyz = new float[3];
            xyz[0] = xyz[1] = xyz[2] = 0f;
            for (int i = 0; i < 3; ++i)
            {
                xyz[0] += XWeight[i] * this[i];
                xyz[1] += YWeight[i] * this[i];
                xyz[2] += ZWeight[i] * this[i];
            }

            return xyz;
        }




        public static float[] XWeight = new[] { 0.412453f, 0.357580f, 0.180423f };
        public static float[] YWeight = new[] { 0.212671f, 0.715160f, 0.072169f };
        public static float[] ZWeight = new[] { 0.019334f, 0.119193f, 0.950227f };

        public static float[] rWeight = new[] { 3.240479f, -1.537150f, -0.498535f };
        public static float[] gWeight = new[] { -0.969256f, 1.875991f, 0.041556f };
        public static float[] bWeight = new[] { 0.055648f, -0.0204043f, 1.057311f };

        public float Filter()
        {
            return Math.Max(c1, Math.Max(c2, c3));
        }




        public static bool operator >(RgbSpectrumInfo s, RgbSpectrumInfo s1)
        {
            return s.y() > s1.y();
        }

        public static bool operator <(RgbSpectrumInfo s, RgbSpectrumInfo s1)
        {
            return s.y() < s1.y();
        }

        #region IComparable<RgbSpectrum> Members

        public int CompareTo(RgbSpectrum other)
        {
            return this.y().CompareTo(other.y());
        }

        #endregion

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public RgbSpectrumInfo MADD(ref RgbSpectrumInfo arg)
        {
            this.c1 += this.c1 * arg.c1;
            this.c2 += this.c2 * arg.c2;
            this.c3 += this.c3 * arg.c3;
            return this;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void MAdd(ref RgbSpectrumInfo arg, float f)
        {
            this.c1 += f * arg.c1;
            this.c2 += f * arg.c2;
            this.c3 += f * arg.c3;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void MAdd(ref RgbSpectrumInfo arg, ref RgbSpectrum ths)
        {
            this.c1 += ths.c1 * arg.c1;
            this.c2 += ths.c2 * arg.c2;
            this.c3 += ths.c3 * arg.c3;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public RgbSpectrumInfo MADD(RgbSpectrumInfo arg)
        {
            this.c1 += this.c1 * arg.c1;
            this.c2 += this.c2 * arg.c2;
            this.c3 += this.c3 * arg.c3;
            return this;
        }


        public RgbSpectrumInfo Clone()
        {
            return new RgbSpectrumInfo(c1, c2, c3);
        }

        internal bool IsNaN()
        {
            return float.IsNaN(c1) || float.IsNaN(c2) || float.IsNaN(c3);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static RgbSpectrumInfo Max(ref RgbSpectrumInfo ks, ref RgbSpectrumInfo kd)
        {
            if (ks > kd)
            {
                return ks;
            }
            if (kd > ks)
            {
                return kd;
            }
            return kd;
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static RgbSpectrumInfo Max(ref RgbSpectrum ks, ref RgbSpectrum kd)
        {
            if (ks > kd)
            {
                return (RgbSpectrumInfo) ks;
            }
            if (kd > ks)
            {
                return (RgbSpectrumInfo) kd;
            }
            return (RgbSpectrumInfo) kd;
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static RgbSpectrumInfo Max(RgbSpectrumInfo ks, ref RgbSpectrum kd)
        {
            if (ks > (RgbSpectrumInfo) kd)
            {
                return ks;
            }
            if (kd > (RgbSpectrum) ks)
            {
                return (RgbSpectrumInfo) kd;
            }
            return (RgbSpectrumInfo) kd;
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static RgbSpectrumInfo Max(RgbSpectrumInfo ks, RgbSpectrumInfo kd)
        {
            if (ks > kd)
            {
                return ks;
            }
            if (kd > ks)
            {
                return kd;
            }
            return kd;
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Max(ref RgbSpectrumInfo ks, ref RgbSpectrumInfo kd, out RgbSpectrumInfo max)
        {
            max = kd;
            if (ks > kd)
            {
                max = ks;
            }
            if (kd > ks)
            {
                max = kd;
            }
        }

        public RgbSpectrumInfo Normalize()
        {
            return this / y();
        }

        public byte[] Serialize()
        {
            return new[]
                {
                    BitConverter.GetBytes(c1),
                    BitConverter.GetBytes(c2),
                    BitConverter.GetBytes(c3)
                }.SelectMany(x => x).ToArray();
        }

        public void Deserialize(byte[] data, int offset = 0)
        {
            c1 = BitConverter.ToSingle(data, 0 + offset);
            c2 = BitConverter.ToSingle(data, 4 + offset);
            c3 = BitConverter.ToSingle(data, 8 + offset);
        }

        public void DeGamma()
        {
            c1 = (float)(Math.Log(1.001f - c1));
            c2 = (float)(Math.Log(1.001f - c2));
            c3 = (float)(Math.Log(1.001f - c3));
        }

        public string SerializeToString()
        {
            return string.Format("{0:F4},{1:F4},{2:F4}", c1, c2, c3);
        }

        public void FromString(string v)
        {
            var dta = v.Trim().Replace(",", ".").Split(' ');
            if (dta.Length != 3)
            {
                throw new ArgumentException("string is in incorrect format {0}", v);
            }
            c1 = float.Parse(dta[0], CultureInfo.InvariantCulture);
            c2 = float.Parse(dta[1], CultureInfo.InvariantCulture);
            c3 = float.Parse(dta[2], CultureInfo.InvariantCulture);
        }

        public static RgbSpectrumInfo FromStringData(string v)
        {
            var dta = v.Trim('{', '}').Replace(",", " ").Split(' ');
            if (dta.Length != 3)
            {
                throw new ArgumentException("string is in incorrect format {0}", v);
            }
            return new RgbSpectrumInfo()
                {
                    c1 = float.Parse(dta[0], CultureInfo.InvariantCulture),
                    c2 = float.Parse(dta[1], CultureInfo.InvariantCulture),
                    c3 = float.Parse(dta[2], CultureInfo.InvariantCulture)
                };
        }

        public float Max()
        {
            return Math.Max(c1, Math.Max(c2, c3));
        }

        public RgbSpectrumInfo MaxAsSingle()
        {
            if (c1 > c2 && c1 > c3)
            {
                return new RgbSpectrumInfo(c1, 0, 0f);
            }

            if (c2 > c1 && c2 > c3)
            {
                return new RgbSpectrumInfo(0f, c2, 0f);
            }

            if (c3 > c1 && c3 > c1)
            {
                return new RgbSpectrumInfo(0f, 0f, c3);
            }

            return this;
        }
    }
}